/**
 * 
 */
package com.vision.core.sv.serv.dispatch;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.SQLException;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;

import net.customware.gwt.dispatch.client.standard.StandardDispatchService;
import net.customware.gwt.dispatch.server.Dispatch;
import net.customware.gwt.dispatch.shared.Action;
import net.customware.gwt.dispatch.shared.DispatchException;
import net.customware.gwt.dispatch.shared.Result;
import net.sf.gilead.gwt.GileadRPCHelper;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;

import com.google.gwt.user.client.rpc.IncompatibleRemoteServiceException;
import com.google.gwt.user.client.rpc.SerializationException;
import com.google.gwt.user.server.rpc.RPC;
import com.google.gwt.user.server.rpc.RPCRequest;
import com.vision.core.cm.exception.RpcException;
import com.vision.core.sv.serv.BaseRpcServlet;
import com.vision.core.sv.util.DB;

/**
 * Servlet to handle GWT-Dispatch calls.
 * 
 * @author Mark
 *
 */
public class GileadDispatchServlet extends BaseRpcServlet implements StandardDispatchService {

	private static final long serialVersionUID = 1L;
	
	private static final Logger log = LoggerFactory.getLogger(GileadDispatchServlet.class);
	
	private Dispatch dispatch;
	
	@Override
	public void init(ServletConfig config) throws ServletException {
		super.init(config);
		
		WebApplicationContext ctx = WebApplicationContextUtils
                .getRequiredWebApplicationContext(config.getServletContext());
        AutowireCapableBeanFactory beanFactory = ctx.getAutowireCapableBeanFactory();
        beanFactory.autowireBean(this);
        
        
        // Initialize Gilead
        initGilead();
	}
	
	@Override
	public String processCall(String payload) throws SerializationException {
		RPCRequest rpcRequest = null;
		
		try {
			rpcRequest = RPC.decodeRequest(payload, this.getClass(), this);
			
			Method method = rpcRequest.getMethod();
			
			Object returnValue = method.invoke(this, rpcRequest.getParameters());
			returnValue = GileadRPCHelper.parseReturnValue(returnValue, getBeanManager());
			
			String rpcResult = RPC.encodeResponseForSuccess(method, returnValue, rpcRequest.getSerializationPolicy());
			return rpcResult;
		}
		catch (InvocationTargetException e) {
    		log.error(e.getMessage(), e);
    		
    		SQLException sqle = DB.getSQLException(e.getTargetException());
    		if (sqle != null)
    			log.error(sqle.getMessage(), sqle);
    		
    		if (e.getTargetException() instanceof RpcException)
				return RPC.encodeResponseForFailure(rpcRequest.getMethod(), e.getTargetException(), rpcRequest.getSerializationPolicy());
    		
    		if (e.getTargetException() instanceof com.vision.core.cm.exception.DispatchException)
				return RPC.encodeResponseForFailure(rpcRequest.getMethod(), e.getTargetException(), rpcRequest.getSerializationPolicy());
    		
    		return RPC.encodeResponseForFailure(null, e);
    	} catch (IllegalAccessException e) {
    		log.error("An IllegalAccessException was thrown while processing this call.", e);
    		return RPC.encodeResponseForFailure(null, e);
    	} catch (IncompatibleRemoteServiceException e) {
    		log.error("An IncompatibleRemoteServiceException was thrown while processing this call.", e);
    		return RPC.encodeResponseForFailure(null, e);
    	}
	}

	@Override
	public Result execute(Action<?> action) throws DispatchException {
		try {
			log.debug("Executing action: {}", action);
			
			Result result = dispatch.execute(action);
			return result;
		} catch (RuntimeException e) {
			throw e;
		}
	}
	
	@Autowired
	@RequestMapping
	public void setDispatch(Dispatch dispatch) {
		this.dispatch = dispatch;
	}

}
